#include "NetworkClient.h"

unsigned short NetworkClient::useCount = 0;

NetworkClient::NetworkClient(const char* host, unsigned short port, NetworkListener* listener) {
    std::string str = host;
    
    this->host = (char*)str.c_str();
    this->port = port;
    active = false;
    running = false;
    reconnect = false;
    data = new char[NETWORK_MAX_RECEIVE_BUFFER]; // Casi medio MB
    
    this->listener = listener;
    if (listener == NULL) {
        //TODO: Arrojar excepcion de OGRE
        return;
    }
    
    int result = 0;
    if (NetworkClient::useCount++ == 0) {
        WSAData wsadata;
        result = WSAStartup(MAKEWORD(2, 2), &wsadata);
    }
    
    if (result == 0) {
        mSocket = socket(AF_INET, SOCK_STREAM, 0);
        if (mSocket == INVALID_SOCKET) {
            listener->connectionFailed(this);
        } else {
            sockaddr_in address;
            
            address.sin_port = htons(port);
            address.sin_addr.s_addr = inet_addr(host);
            address.sin_family = AF_INET;
            
            result = connect(mSocket, (sockaddr*)&address, sizeof(address));
            if (result == SOCKET_ERROR) {
                listener->connectionFailed(this);
            } else {
                nextReceiveSize = listener->connectionMade(this);
                
                active = true;
                running = true;

				runThread(this);
            }
        }
    }
}

NetworkClient::~NetworkClient() {
    close();
    
    delete data;
    
    if (--NetworkClient::useCount == 0) {
        WSACleanup();
    }
}

bool NetworkClient::receive() {
    if (running) {
        if (!active) {
            if (reconnect) {
                sockaddr_in address;

                address.sin_port = htons(port);
                address.sin_addr.s_addr = inet_addr(host);
                address.sin_family = AF_INET;

                int result = connect(mSocket, (sockaddr*)&address, sizeof(address));
                if (result == SOCKET_ERROR) {
                    reconnect = listener->connectionLost(this);
                } else {
                    nextReceiveSize = listener->connectionRestored(this);

					runThread(this);

                    active = true;
                    running = true;
                }
            } else {
                close();
            }
        } else {
            if (nextReceiveSize > 0) {
                unsigned long receiveBufferSize = 0;
                if (ioctlsocket(mSocket, FIONREAD, &receiveBufferSize) == 0) {
                    if (nextReceiveSize <= receiveBufferSize) {
                        unsigned long recSize = recv(mSocket, data, nextReceiveSize, 0);
                        if (recSize == 0) {
                            active = false;
                            reconnect = listener->connectionLost(this);
                        } else {
                            assert(recSize == nextReceiveSize);
                            nextReceiveSize = listener->dataReceived(this, data, recSize);
                        }
                    }
                }
            } else {
                unsigned long recSize = recv(mSocket, data, NETWORK_MAX_RECEIVE_BUFFER, 0);
                if (recSize == 0) {
                    active = false;
                    reconnect = listener->connectionLost(this);
                } else {
                    nextReceiveSize = listener->dataReceived(this, data, recSize);
                }
            }
        }
    }
    
    return running;
}

bool NetworkClient::send(char* data, unsigned long length) {
    char* local = data;
    if (running && active) {
        unsigned long remaining = length;
        int sent = 0;
        while (true) {
            sent = ::send(mSocket, data, length, 0);
            if (sent == SOCKET_ERROR) {
                return true;
            } else {
                return false;
            }
        }
    } else if (running && !active && reconnect) {
        return true;
    } else {
        return false;
    }
}

void NetworkClient::close() {
    if (running) {
        running = false;
        closesocket(mSocket);
    }
}

bool NetworkClient::is_running() {
    return running;
}

int NetworkClient::run() {
    while (this->receive()) {
		Yield();
    }

	return 0;
}
